using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Obsidian;
using System.Reflection;
using Obsidian.GameObjects;
using Obsidian.Components;
using XConsoleApp;
using System.Globalization;

namespace ObsidianTestbed
{

    //using Gearset;

    //public static class Debug
    //{
    //    public static GearConsole GS { get; set; }
    //}

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        Scene scene;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this)
            {
                SynchronizeWithVerticalRetrace = false,
                PreferMultiSampling = true,
            };
            IsFixedTimeStep = true;
            Content.RootDirectory = "Content";

            var asm = Assembly.GetAssembly(typeof(Obsidian.Scene));

           this.Window.Title = "Obsidian: " + asm.GetName().Version.ToString();

           scene = new Scene(this);
        }

        protected override void Initialize()
        {
            scene.Init();

            XConsole.Instance.Height = 25;
            //XConsole.Instance.StringParser = new DemoStringParser();

            //Debug.GS = new GearConsole(this);
            //Debug.GS.Initialize();

            base.Initialize();
        }

        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // create camera
            var mainCamera = new GameObject("MainCamera");

            mainCamera.Transform = new Transform()
            {
                LocalPosition = new Vector2(0, 0),
            };
            mainCamera.Camera = new Camera()
            {
                Orthographic = true,
                OrthographicSize = new Vector2(100, 80),
                Enabled = true,
            };

            // create head
            var head = new GameObject("head")
            {
                Transform = new Transform()
                {
                    LocalPosition = new Vector2(300, 100),
                },
                Renderer = new SpriteRenderer()
                {
                    Frame = new Frame(Content.Load<Texture2D>("head1")),
                },
            };

            //head.Components.Add(new Spin());

            // create mouth
            var mouth = new GameObject("mouth");

            mouth.Transform = new Transform()
            {
                LocalPosition = new Vector2(0, 0),
            };
            mouth.Renderer = new SpriteRenderer()
            {
                Frame = new Frame(Content.Load<Texture2D>("head2")),
            };

            mouth.Components.Add(new Spin());
            mouth.Components.Add(new Wiggle());

            head.Children.Add(mouth);

            var garbageTextSprite = new GameObject("garbageSprite")
            {
                Transform = new Transform()
                {
                    LocalPosition = new Vector2(400, 50),
                },
                Renderer = new NumberRenderer()
                {
                    Font = Content.Load<SpriteFont>("font"),
                },
            };

            // test
            garbageTextSprite.Components.Add(new Transform());

            garbageTextSprite.Components.Add(new GetGarbage());
            garbageTextSprite.Components.Add(new Wiggle());

            // create a multiple linked set of objects
            var chain = new GameObject("chain")
            {
                Transform = new Transform()
                {
                    LocalPosition = new Vector2(50, 300),
                },
                Renderer = new TextRenderer()
                {
                    Font = Content.Load<SpriteFont>("font"),
                    Text = "C",
                },
            };
            var script = new ChangeText();
            script.index = 0;
            chain.Components.Add(script);
            var parent = chain;

            for (int i = 0; i < 12; i++)
            {
                var child = new GameObject(i.ToString())
                {
                    Transform  = new Transform(),
                    Renderer = new TextRenderer()
                    {
                        Font = Content.Load<SpriteFont>("font"),
                        Text = i.ToString(),
                    },
                };
                child.Transform.LocalPosition = new Vector2(12, 0);

                child.Components.Add(new Rotate());
                script = new ChangeText();
                script.index = i + 1;
                child.Components.Add(script);
                parent.Children.Add(child);
                parent = child;
            }

            scene.RootGameObjects.Add(mainCamera);
            scene.RootGameObjects.Add(head);
            scene.RootGameObjects.Add(garbageTextSprite);
            scene.RootGameObjects.Add(chain);

            scene.Initialize();

            // Add our game to the console's selected objects list
            XConsole.Instance.SelectedObjects.Add(chain);
            
            GC.Collect();
        }

        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // update time
            Time.TotalTime = (float)gameTime.TotalGameTime.TotalSeconds;
            Time.DeltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;

            scene.Update();

            //Debug.GS.Update(gameTime);

            base.Update(gameTime);
        }

       
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            scene.RenderScene();

            //Debug.GS.Draw(gameTime);

            base.Draw(gameTime);
        }
    }

   public class ChangeText : Script
    {
        static string helloWorld = "  Hello World  ";
        public int index;
        float time;

        public override void Update()
        {
            var renderer = GameObject.Renderer as TextRenderer;

            time += Time.DeltaTime;

            if (time > 0.25f)
            {
                var chars = helloWorld.ToCharArray();

                if (index >= helloWorld.Length)
                    index = 0;

                renderer.Text = new string(chars[index], 1);
                time = 0;
                index++;
            }
        }
    }

    class GetGarbage : Script
    {
        public override void Update()
        {
            var renderer = GameObject.Renderer as NumberRenderer;

            renderer.Value = (int)GC.GetTotalMemory(false);
        }
    }

    class Wiggle : Script
    {
        Vector2 position;

        public override void Awake()
        {
            position = Transform.LocalPosition;
        }

        public override void Update()
        {  
            Transform.LocalPosition = position + (new Vector2((float)(Math.Sin(Time.TotalTime * 6) * 2f), (float)(Math.Cos(Time.TotalTime * 8) * 4f)));
            Transform.LocalRotation = (float)Math.Abs((Math.Sin(Time.TotalTime * 8) * 0.015));
        }
    }

    class Spin : Script
    {
        public override void Update()
        {
            Transform.LocalRotation += 0.01f;
        }
    }

    class Rotate : Script
    {
        public override void Update()
        {
            Transform.LocalRotation = (float)Math.Cos(Time.TotalTime) * 0.05f;
        }
    }

    public class DemoStringParser : XConsoleApp.XConsole.IStringParser
    {
        public object Parse(Type type, string value)
        {
            if (type == typeof(GameObject))
            {
                PropertyInfo p = typeof(GameObject).GetProperty(value, BindingFlags.Static | BindingFlags.Public);
                return p.GetValue(null, null);
            }
            else if (type == typeof(int))
            {
                return int.Parse(value, CultureInfo.InvariantCulture);
            }
            else
            {
                throw new InvalidOperationException(string.Format("{0} not supported by DemoStringParser", type.Name));
            }
        }

        public string ToString(object value)
        {
            // Here you can get the type of the value and do some very intelligent string construction so that the console will
            // write values however you want. Since we're lazy (and this is a demo), we'll just use the standard ToString() method.
            return value.ToString();
        }
    }
}
